﻿using System;
using System.Collections;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;

using alib;
using alib.Debugging;
using alib.Enumerable;

using agree.configuration;

namespace agree
{
	public sealed partial class GenControl : ChartControl<GenControl, CoverageMap>
	{
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public sealed class ChartCell : _chart_cell
		{
			public struct _enum_0_n_incl : IEnumerator<IChartObj>
			{
				ChartCell[] rgcc;
				readonly int _upr_limit_incl;
				ulong exclude;
				IChartObj<CoverageMap> cur;
				int i_col;

				public _enum_0_n_incl(ChartCell[] rgcc, int c_covered, ulong exclude)
				{
					this.rgcc = rgcc;
					this.exclude = exclude;
					this._upr_limit_incl = rgcc.Length - c_covered - 1;
					cur = null;
					i_col = 0;
				}

				public void Reset()
				{
					i_col = 0;
					cur = null;
				}

				public bool MoveNext()
				{
					do
					{
						while ((cur = (IChartObj<CoverageMap>)(cur == null ? rgcc[i_col].m_first : cur.Next)) != null)
						{
							// note: assuming CoverageMap is correct (enough) for non-active objects
							if (exclude == 0 || (cur.ProximalContext.covered_eps & exclude) == 0)
							{
								if (cur.WaitActive())
									return true;
							}
						}
					}
					while (++i_col <= _upr_limit_incl);
					return false;
				}

				public IChartObj Current { get { return cur; } }
				object IEnumerator.Current { get { return cur; } }
				public void Dispose() { cur = null; rgcc = null; }
			};

			public struct _enum_gen_pack_cnd : IEnumerator<IChartObj>
			{
				IChartObj first;
				IChartObj<CoverageMap> cur, exclude;
				CoverageMap cm_excl;

				public _enum_gen_pack_cnd(_chart_cell cc, IChartObj<CoverageMap> exclude_obj)
				{
					this.first = cc.m_first;
					this.exclude = exclude_obj;
					this.cm_excl = exclude_obj.ProximalContext;
					this.cur = null;
				}

				public bool MoveNext()
				{
					cur = (IChartObj<CoverageMap>)(cur == null ? first : cur.Next);
					while (cur != null)
					{
						if (cur != exclude)
						{
							// note: assuming CoverageMap is correct (enough) for non-active objects
							/// for packing, all of the coverage situation must be identical
							CoverageMap cm_cur = cur.ProximalContext;
							CoverageMap cm = exclude.ProximalContext;
							if (cm_cur.covered_eps == cm_excl.covered_eps &&
								cm_cur.access_vars == cm_excl.access_vars)
								return true;
						}
						cur = (IChartObj<CoverageMap>)cur.Next;
					}
					return false;
				}

				public void Reset() { this.cur = null; }
				public IChartObj Current { get { return cur; } }
				object IEnumerator.Current { get { return cur; } }
				public void Dispose() { first = cur = exclude = null; }
			};
		};

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// From: Woodley Packard
		/// Sent: Friday, November 04, 2011 6:21 PM
		///
		/// [For generation] deploying [spanning-only rules] in an "optimal" way would be challenging. The goal is that
		/// these rules are only applied as parts of the unary chain at the top of a tree. The problem is that, in 
		/// generation, it is very difficult to tell whether you have reached the unary chain at the top of the tree.
		/// Even if you have used up all the input EPs, you could conceivably add a vacuous word to the left or right 
		/// side of the result. And even if you haven't yet used up all the input EPs, you might be at the top 
		/// because unary rules with C-CONTs can supply them.
		///
		/// One circumstance when you can say for certain that you aren't at the top is when there are remaining
		/// input EPs that you can prove cannot be supplied by a unary rule's C-CONT. In these cases, you can 
		/// safely refuse to apply spanning-only rules, I think. The ERG has a (consistent, I believe) 
		/// convention of putting an underscore as the first character of relation names that are lexical
		/// (i.e. come from a lexeme). One approach would be to rely on that. Alternately, you could 
		/// keep track while accessing the semantic index and record for each EP whether it was supplied 
		/// only by lexemes or whether rules' C- CONTs can supply it.
		///
		/// In the remaining cases, you have to be willing to apply those spanning- only rules. You could keep 
		/// track of whether a particular edge has any spanning-only rules in its derivation, and refuse to build 
		/// non-unary derivations on top of it if so. The question of how/whether to pack an edge marked as 
		/// spanning-only with an edge marked as not spanning- only would require some thought though, so it's
		/// unclear whether this would be a net win.			
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override bool CheckRuleGeometry(IActiveRule ir, ref CoverageMap new_coverage)
		{
			var ihpc = ir as IProximalContext<CoverageMap>;
			return ihpc == null || ihpc.CombineWith(new_coverage, out new_coverage);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override void AddActiveEdge(ActiveEdge ae)
		{
			rgns[ae.ctx.c_covered].Subscribe(ae);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override IEnumerator<IChartObj> GetPassiveForActive(ActiveEdge ae)
		{
			return new ChartCell._enum_0_n_incl(chart, ae.ctx.c_covered, ae.ctx.covered_eps);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override void SendToPassiveEdges(IChartObj<CoverageMap> po)
		{
			for (int ix = spanning_count - po.ProximalContext.c_covered; ix >= 0; --ix)
				rgns[ix].SendToAllSubscribers(po);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// Index accessibility filtering
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override bool CheckPassiveAccessibility(ArrayTfs new_mother, ref CoverageMap ctx_new)
		{
			bool b = ctx_new.RecomputeAccessible(new_mother);
			if (b)
				IndexAccessibilityRejection();
			return b;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override IChartObj CreateChartObj(
			Rule rule,
			ArrayTfs tfs,
			IReadOnlyList<IChartObj> rgo,
			CoverageMap cm,
			StartSymbol[] ss_matches)
		{
			//Debug.Print("Creating chart obj with rule: {0}, ctx: {1}", rule.Name, cm);

			return tfs.SemanticsRestricted ? (IChartObj)
				new gxo_nary_pack(this, rule, tfs, rgo, cm, ss_matches) :
				new gxo_nary(this, rule, tfs, rgo, cm, ss_matches);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override void AddChartObj(IChartObj<CoverageMap> po)
		{
			chart[po.ProximalContext.c_covered].Add(po, ref next_obj_id);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override IEnumerator<IChartObj> GetPackingCandidates(IChartObj _po)
		{
			var po = (IChartObj<CoverageMap>)_po;
			return new ChartCell._enum_gen_pack_cnd(chart[po.ProximalContext.c_covered], po);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override IEnumerator<IChartObj> ItemEdges(CoverageMap ctx)
		{
			return new ChartCell._enum(chart[ctx.c_covered]);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public override IEnumerable<IChartObj> AllEdges()
		{
			Wait();

			var e = new ChartCell._enum_0_n_incl(chart, 0, 0);
			while (e.MoveNext())
				yield return e.Current;
		}

		public override String ToString()
		{
			return "Generation chart ";// +ctrl.SysObjName;
		}
	};
}
